# coding:utf-8
import glob
import traceback

import os
import re
import shutil
import socket

import time
from abc import abstractmethod, ABCMeta
from flask import logging
from pexpect import ExceptionPexpect

from pexpect import run

from app.modules.common import network
from app.modules.common.port import protected_port_group_pool
from app.modules.common.utils import LogProxy
from config import BeetleConfig

logger = logging.getLogger(__name__)


# 抽象需要容器和代码一起运行的可发布应用系统.
class ContainerApp:
    __metaclass__ = ABCMeta

    def __init__(self, app_id, app_name, zone_home_path, zone_alias, port,
                 container_home, nas_app_package_path=None,
                 app_config_home_path=None, sub_path=''):
        self._app_id = app_id
        self._container_home = container_home
        self._port = int(port)

        self._zone_alias = zone_alias
        self._zone_home_path = zone_home_path
        self._zone_name = re.sub('(.*/)(.*)', r'\2', zone_home_path)

        self._app_name = app_name
        self._app_zone_alias = self._app_name + '-' + self._zone_alias

        self._nas_app_package_path = nas_app_package_path

        self._config_home_path = '/usr/local/config'

        if nas_app_package_path is None:
            self._nas_app_package_path = '/mnt/winitRelease/latest.product/' + self._app_name

        if app_config_home_path is None:
            self._app_config_home_path = self._config_home_path + '/' + self._zone_name + '/' + self._app_name

        self._local_ip = network.ip

        self._version = '-1'
        self._sub_path = sub_path
        self.set_package_create_date()

        self._ports = [port]

    def set_runtime_param(self, nas_app_package=None, version='', sub_path=''):

        self.app_log('配置应用的运行时参数:nas_app_package=%s version=%s sub_path=%s' % (
            nas_app_package, version, sub_path))

        if nas_app_package is None or nas_app_package == '':
            nas_app_package = BeetleConfig.PACKAGE_LATEST_NAS_PATH + '/' + self._app_name
            version = self.get_version_by_version_file()
        if sub_path is None:
            sub_path = ''

        self._nas_app_package_path = nas_app_package
        self._sub_path = sub_path
        self._version = version

        self.set_package_create_date()

        self.app_log('nas_app_package_path变更为:%s' % self._nas_app_package_path)

        self.app_log(
            'package_create_date变更为:%s' % self._app_package_create_date)

    # 安装应用
    @abstractmethod
    def install_app(self):
        pass

    # 重启应用
    @abstractmethod
    def restart(self):
        self.stop();

    # 校验当前容器的正确性
    @abstractmethod
    def validate(self):
        pass

    # 替换容器应用的环境配置文件
    @abstractmethod
    def _config_app(self):
        pass

    def get_ports(self):
        return self._ports

    def _update_config(self):

        self.run(
            '/bin/bash -c "cd %s;svn revert -R ./ ;svn up;"'
            % self._config_home_path)

    # 停止应用(按照容器路径作为线索, 杀掉所有相关进程)
    def stop(self):
        _cmd = '/bin/bash -c "ps -ef |grep -w \'%s\' |grep -v \'grep\' |awk \'{print $2}\'|xargs kill -9"' % self._container_home
        # _cmd = '/bin/bash -c "lsof -i:%s |grep -i LISTEN|awk \'{print $2}\'|xargs kill -9"' % str(self._port)
        self.run(command=_cmd, timeout=10)
        self.app_log('%s has been stoped.' % self._app_id)
        time.sleep(1)

    # 删除应用
    def uninstall_app(self):
        self.validate()
        self.stop()

        if os.path.exists(self._container_home):
            shutil.rmtree(self._container_home)
        self.app_log('Directory %s has been removed.' % self._container_home)

    def chmod(self):
        # self.run('chown -R beetle %s' % self._container_home)
        # self.app_log('%s config file has been changed successfully.' % self.app_id)
        pass

    # 根据端口杀掉可能冲突的应用
    def kill_conflict_port_progress(self, *ports):
        for port in ports:
            self.run(
                command='/bin/bash -c "lsof -i:%s|grep -i LISTEN|awk \'{print $2}\'|xargs kill -9 ;"' % port,
                raise_on_error=False)
            self.run(
                command='/bin/bash -c "lsof -i:%s|grep -i ESTABLISHED|awk \'{print $2}\'|xargs kill -9 ;"' % port,
                raise_on_error=False)

    def svn_update(self):
        self.run(command='')

    def is_started(self, accurately=False):
        """
        判断应用是否启动的接口

        Args:
            accurately:
             True: 精确判断, 用应用的上下文搜索进程, 有的话就算启动. 这种速度稍微慢一点.
             Fase: 简单判断,用应用端口是否(没有被保护且端口已经打开)

        Returns:

        """
        if not accurately:
            # 端口被保护, 或者端口没有打开,证明应用没有启动.因为大部分情况下
            return (not protected_port_group_pool.is_group_been_protected(
                self._app_id)) and network.is_open(
                self._port)
        else:
            #
            return network.is_exists(self._container_home + '/')

    # 打印应用的日志,为了区别对待应用, 对应用容器的控制日志被分别放到了单独的文件内.
    def app_log(self, content='', is_error=False):

        if not os.path.exists(BeetleConfig.PIPELINE_APP_LOG_PATH):
            run(
                '/bin/bash -c "mkdir -p %s"' % BeetleConfig.PIPELINE_APP_LOG_PATH)

        logfile = BeetleConfig.PIPELINE_APP_LOG_PATH + '/%s.log' % self._app_id
        if is_error:
            log_info = '[ERROR] %s [at %s] \n' % (
                content, time.strftime("%Y-%m-%d %H:%M:%S",
                                       time.localtime(time.time())))
        else:
            log_info = '[INFO] %s [at %s] \n' % (
                content, time.strftime("%Y-%m-%d %H:%M:%S",
                                       time.localtime(time.time())))

        logger.info(log_info)
        print log_info

        with open(logfile, 'a') as _f:
            _f.write(log_info)

    # 安装应用
    def clear_app_log(self, content='', is_error=False):

        logger.info(content)

        if not os.path.exists(BeetleConfig.PIPELINE_APP_LOG_PATH):
            run(
                '/bin/bash -c "mkdir -p %s"' % BeetleConfig.PIPELINE_APP_LOG_PATH)

        logfile = BeetleConfig.PIPELINE_APP_LOG_PATH + '/%s.log' % self._app_id

        with open(logfile, 'w') as _f:
            _f.write('')

    def run(self, command, withexitstatus=1, cwd=None, raise_on_error=False,
            timeout=3000, logfile=None):

        self.app_log('Run command:%s' % command)

        msg = ''
        status = 0
        try:
            msg, status = run(command, withexitstatus=withexitstatus, cwd=cwd,
                              timeout=timeout, logfile=logfile)
        except ExceptionPexpect, e:
            if raise_on_error:
                self.app_log('[WARN]执行可能不正确, 返回结果Result:%s' % e.message)
                raise e
            else:
                logger.warn(traceback.format_exc())
        finally:
            self.app_log('Result:%s' % msg)

        if raise_on_error and not status == 0:
            self.app_log('[WARN]执行可能不正确, 返回结果Result:%s' % msg)
            # raise BaseException(msg)

        self.app_log('Result:%s' % msg)

        return msg, status

    def set_package_create_date(self):

        _result = re.search("\\d{8}-\\d{6}", self._version, re.M)

        if _result is not None:
            self._app_package_create_date = _result.group(0)
        else:
            _package_path = self._nas_app_package_path + self._sub_path
            if os.path.isdir(_package_path):
                _date = os.path.getmtime(_package_path)
            else:
                # 如果路径不存在, 为保证变量别为None, 填充系统当前时间.
                _date = time.time()

            self._app_package_create_date = time.strftime("%Y%m%d-%H%M%S",
                                                          time.localtime(
                                                              _date))

    def get_version_by_version_file(self):

        _path = self._nas_app_package_path

        _version = '-1'

        if str(_path).startswith('/mnt/winitRelease/latest.'):
            fs = glob.glob(_path + '/*.version')
            if len(fs) == 0:
                _version = '-1'
            else:
                _version = os.path.basename(fs[0])
        logger.info('Defalut version is: %s' % _version)
        return _version

    def replace_config_content(self, file_path, pattern, repl):
        _config_path = file_path

        if not os.path.isfile(file_path):
            return

        with open(_config_path, 'r') as _f:
            _config_content = _f.read()

            _config_content = re.sub(pattern, repl,
                                     _config_content)
        with open(_config_path, 'w') as _f:
            _f.write(_config_content)
